package controller;

import java.io.IOException;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Image;
import javax.microedition.midlet.MIDlet;

import model.BluetoothContacts;
import model.ContactFactory;
import model.MsgContact;
import view.View;
import base.Comm;
import base.Contacts;
import base.MessageListener;
import base.ScreenActionListener;

import communication.CommFactory;

import exceptions.InitializedException;

public final class Controller {

	// Command Action codes
	/*
	 * commands sent by the view module, these are the commands that the
	 * controller knows
	 */
	public static final byte EXIT_COMMAND = 0x00;
	public static final byte OPTIONS_COMMAND = 0x01;
	public static final byte BACK_COMMAND = 0x02;
	public static final byte SELECT_COMMAND = 0x03;
	public static final byte CONNECT_COMMAND = 0x04;
	public static final byte ADD_COMMAND = 0x05;
	public static final byte REMOVE_COMMAND = 0x06;
	public static final byte SEND_COMMAND = 0x07;
	public static final byte CONFIRM_COMMAND = 0x08;
	public static final byte IMAGE_COMMAND = 0x09;
	public static final byte NEXT_COMMAND = 0x0A;

	// images location path
	private static final StringBuffer IMAGE_PATH = new StringBuffer("/avatar_@.png");
	private static final String[] IMAGES_NAME = { "avatar_0", "avatar_1", "avatar_2", "avatar_3", "avatar_4", "avatar_5", "avatar_6", "avatar_7" };
	private static final Image[] IMAGES = new Image[8];

	// screen information
	// contact location - action codes
	private static final byte SEARCH_CONTACTS = 0;
	private static final byte STORED_CONTACTS = 1;
	// transitions
	private static final String[] SEARCHING = { "Searching..." };
	private static final String[] LOADING = { "Loading..." };

	// application modules
	private final View appView;
	private final Contacts appModel;
	private final MIDlet appMIDlet;

	// own variables
	private MsgContact own;
	
	// communication
	private Comm appComm;
	private int currentComm;

	// state variables
	private byte currentScreen;
	private int intBuffer;
	private volatile boolean connected;
	private ScreenActionListener listener;
	private MsgContact[] contacts;
	private int currentContact;
	private String lastMessage;
	
	private static void fillImageArray() {
		for (int i = 0; i < IMAGES.length; i++) {
			IMAGE_PATH.setCharAt(8, (char) ('0' + i));
			try {
				IMAGES[i] = Image.createImage(IMAGE_PATH.toString());
			} catch (IOException e) {
			}
		}
	}

	public Controller(MIDlet midlet) {
		
		own = new MsgContact("Messenger", null, 0);
		own.id = 0;
		appModel = ContactFactory.getContacts(own, CommFactory.BLUETOOTH);
		appView = new View();
		appMIDlet = midlet;
		try {
			appView.init(Display.getDisplay(midlet));
		} catch (InitializedException e) {
			appView.showError("Already initialized", currentScreen);
		}
	}

	public void init() {
		try {
			currentScreen = View.WELCOME_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch (code) {
						case EXIT_COMMAND:
							appMIDlet.notifyDestroyed();
							break;
						case NEXT_COMMAND:
							configuration();
							break;
						}
					}
				};
				appView.selectScreen(currentScreen, listener);
				// clean up
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Application has gone wild!", View.WELCOME_SCREEN);
		}
	}

	private void configuration() {
		try {
			currentScreen = View.CONFIG_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch (code) {
						case BACK_COMMAND:
							init();
							break;
						case NEXT_COMMAND:
							connections();
							break;
						case IMAGE_COMMAND:
							image();
							break;
						case CONFIRM_COMMAND:
							if (obj != null) {
								own.name = (String) obj;
								appModel.setContact(own);
							}
							break;
						}
					}
				};
				String[] name = { own.name };
				appView.selectScreen(currentScreen, listener, name);
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
		}
	}

	private void connections() {
		try {
			currentScreen = View.CONNECTION_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch (code) {
						case BACK_COMMAND:
							init();
							break;
						case SELECT_COMMAND:
							currentComm = ((Integer) obj).intValue();
							appComm = CommFactory.getComm(currentComm);
							try {
								appComm.init(own.name, own.image);
							} catch (InitializedException e) {
								appView.showError("Application already initialized", View.WELCOME_SCREEN);
							}
							
							appComm.startReceiver(new Message());
							locations();
							break;
						}
					}
				};
				appView.selectScreen(currentScreen, listener,
						CommFactory.AVAILABLE_COMMS);
				// clean up
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
		}
	}

	private void locations() {
		try {
			currentScreen = View.CONTACTS_SOURCE_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch (code) {
						case BACK_COMMAND:
							init();
							break;
						case SELECT_COMMAND:
							intBuffer = ((Integer) obj).intValue();
							try {
								switch (intBuffer) {
								case SEARCH_CONTACTS:
									appView.selectScreen(View.INFO_SCREEN, null, SEARCHING);
									Thread.sleep(10000);//
									contacts = appComm.search();
									if (contacts == null)
										throw new Exception("contacts not found");
									contacts(true);
									break;
								case STORED_CONTACTS:
									appView.selectScreen(View.INFO_SCREEN, null, LOADING);
									contacts = appModel.getAllContacts();
									if (contacts == null)
										throw new Exception("contacts not present in storage");
									contacts(false);
									break;
								}
								break;
							} catch (Exception e) {
								appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
							}
						}
					}
				};
				appView.selectScreen(currentScreen, listener);
				// clean up
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
		}
	}

	private void image() {
		try {
			currentScreen = View.IMAGE_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch(code) {
						case BACK_COMMAND:
							configuration();
							break;
						case SELECT_COMMAND:
							intBuffer = ((Integer) obj).intValue();
							own.image = intBuffer;
							appModel.setContact(own);
							configuration();
							break;
						}
					}
				};
				if (IMAGES[0] == null)
					fillImageArray();
				appView.selectScreen(currentScreen, listener, IMAGES_NAME,
						IMAGES);
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
		}
	}

	private void contacts(final boolean search) {
		try {
			currentScreen = search ? View.CONTACTS_SEARCH_SCREEN
					: View.CONTACTS_DB_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch (code) {
						case BACK_COMMAND:
							locations();
							break;
						case CONNECT_COMMAND:
							currentContact = ((Integer) obj).intValue();
							if (!search)
								appModel.getContact(contacts[currentContact]);
							StringBuffer sb = new StringBuffer(100);
							sb.append(BluetoothContacts.BT_ATTR);
							sb.append(appComm.getAddress());
							sb.append('_');
							sb.append(BluetoothContacts.NAME_ATTR);
							sb.append(own.name);
							sb.append('_');
							sb.append(BluetoothContacts.IMG_ATTR);
							sb.append(own.image);
							sb.append('_');
							appComm.send(contacts[currentContact].url,
									sb.toString());
							messages();
							break;
						case ADD_COMMAND:
							currentContact = ((Integer) obj).intValue();
							appModel.addContact(contacts[currentContact]);
							break;
						case REMOVE_COMMAND:
							currentContact = ((Integer) obj).intValue();
							appModel.removeContact(contacts[currentContact]);
							break;
						}
					}
				};
				appView.selectScreen(currentScreen, listener,
						MsgContact.contactsName(contacts));
				// clean up
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.toString(), View.WELCOME_SCREEN);
		}
	}

	private void messages() {
		try {
			System.gc(); // force garbage collection
			currentScreen = View.MESSENGER_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						switch (code) {
						case BACK_COMMAND:
							init();
							break;
						case SEND_COMMAND:
							writer();
							break;
						}
					}
				};
				if (IMAGES[0] == null)
					fillImageArray();
				Image[] image = { IMAGES[contacts[currentContact].image] };
				appView.selectScreen(currentScreen, listener,
						new String[] { contacts[currentContact].name }, image);
				// clean up
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
		}
	}
	
	private void writer() {
		try {
			currentScreen = View.MESSAGE_SCREEN;
			if (appView.isCached(currentScreen)) {
				appView.selectScreen(currentScreen);
			} else {
				listener = new ScreenActionListener() {
					public void viewAction(int code, Object obj) {
						lastMessage = (String) obj;
						appComm.send(contacts[currentContact].url,
								lastMessage);
						appView.showMsg(null, lastMessage); // first argument commName
						messages();
					}
				};
				appView.selectScreen(currentScreen, listener);
				// clean up
				listener = null;
			}
		} catch (Exception e) {
			appView.showError("Error: " + e.getMessage(), View.WELCOME_SCREEN);
		}
	}
	
	public void cleanup() {
		appComm.cleanup();
		listener = null;
		for (int i=0; i<contacts.length; i++) {
			if (i != currentContact) {
				contacts[i] = null;
			}
		}
		lastMessage = null;
	}

	// message listener
	private class Message implements MessageListener {
		public void recieved(String msg) {
			if (!connected && msg.startsWith(BluetoothContacts.BT_ATTR)) {
				currentContact = 0;
				contacts = new MsgContact[1];
				contacts[currentContact] = new MsgContact(
						BluetoothContacts.getAttribute(msg,
								BluetoothContacts.NAME_ATTR),
						BluetoothContacts.getAttribute(msg,
								BluetoothContacts.BT_ATTR),
						Integer.parseInt(BluetoothContacts.getAttribute(msg,
								BluetoothContacts.IMG_ATTR)));
				connected = true;
				messages();
			} else {
				appView.showMsg(contacts[currentContact].name, msg);
			}
		}
	}
}
